﻿using Microsoft.Xna.Framework.Graphics;

namespace Microsoft.Goldenlight.Framework.Materials
{
    /// <summary>
    /// 	Base class for all materials. Creating a new material is pretty simple, and will generally need to be done each time a new effect is created. There are some exceptions to this. A material can be used for multiple effects if each effect has the same parameters and techniques.
    /// </summary>
    /// <remarks>
    /// 	Also, many effects created using ATI's Render Monkey or Nvidia's FX Composer can be used directly with GenericMaterial. To create a new material, derive from this class and any relevant material interfaces (IReflectionMaterial, IRefractionMaterial, ITextureMaterial, or IFogMaterial). Then, implement OnLoadParameters, OnClearParameters, OnSetupEffect, OnSetupGlobalParameters, and OnSetupObjectParameters. In most cases, this is all that will need to be done, although most of the other methods are virtual as well for the rare case that the base method needs to be extended or changed.
    /// </remarks>
    /// <example>
    /// 	Rendering with a material is done by calling SetupEffect, followed by a SetupPass loop, followed by CleanupEffect. More than likely this will never have to be done, since the render manager already does it, but, for reference: material.SetupEffect(sceneRenderState, materialInstanceData); while (material.SetupPass()) { material.SetupObject(renderInstance, sceneRenderState); // draw the geometry... } material.CleanupEffect();
    /// </example>
    public abstract class Material : LifetimeObject
    {
        #region Fields & Properties

        private bool _isWireframe;

        /// <summary>
        /// 	Whether or not to render the material in wireframe. Only works in debug.
        /// </summary>
        public bool IsWireframe
        {
            get
            {
                return _isWireframe;
            }
            set
            {
                _isWireframe = value;
            }
        }

        private bool _isReflective;

        /// <summary>
        /// 	Whether or not the material is reflective. If this is true, the material must implement IReflectionMaterial. It will be added automatically to the reflection render manager.
        /// </summary>
        public virtual bool IsReflective
        {
            get
            {
                return _isReflective;
            }
            set
            {
                _isReflective = value;
            }
        }

        private bool _isRefractive;

        /// <summary>
        /// 	Whether or not the material is refractive. If this is true, the material must implement IRefractionMaterial. Objects using this material will be automatically added to the refraction render manager.
        /// </summary>
        public virtual bool IsRefractive
        {
            get
            {
                return _isRefractive;
            }
            set
            {
                _isRefractive = value;
            }
        }

        #endregion

        #region Blend Fields & Properties

        private bool _isTranslucent;

        /// <summary>
        /// 	Whether or not the material is translucent.
        /// </summary>
        /// <remarks>
        /// 	Setting this to true causes objects with this material to be rendered with the translucent render manager, which means they are blended and rendered back to front. In 3D, this should be set on any material that has any alpha in its texture. In 2D, this should be set on any material that has translucent parts. i.e an alpha value of something other than 0 or 255. For 2D, this is only relevant when using depth buffering.
        /// </remarks>
        public bool IsTranslucent
        {
            get
            {
                return _isTranslucent;
            }
            set
            {
                _isTranslucent = value;
            }
        }

        private Blend _sourceBlend = Blend.SourceAlpha;

        /// <summary>
        /// 	The source blend factor to use when rendering with this material. This is only applied when IsTranslucent is true.
        /// </summary>
        public Blend SourceBlend
        {
            get
            {
                return _sourceBlend;
            }
            set
            {
                _sourceBlend = value;
            }
        }

        private Blend _destinationBlend = Blend.InverseSourceAlpha;

        /// <summary>
        /// 	The destination blend factor to use when rendering with this material. This is only applied when IsTranslucent is true.
        /// </summary>
        public Blend DestinationBlend
        {
            get
            {
                return _destinationBlend;
            }
            set
            {
                _destinationBlend = value;
            }
        }

        /// <summary>
        /// 	Shortcut for setting additive blending (source = SourceAlpha, destination = One).
        /// </summary>
        public bool IsAdditive
        {
            get
            {
                return _sourceBlend == Blend.SourceAlpha && _destinationBlend == Blend.One;
            }
            set
            {
                _sourceBlend = Blend.SourceAlpha;
                _destinationBlend = value ? Blend.One : Blend.InverseSourceAlpha;
            }
        }

        #endregion

        #region Overrides of LifetimeObject

        #region Implementation of ICloneable

        /// <summary>
        /// 	Called by Clone. Objects need to implement this method to copy all public properties not marked with TorqueCloneIgnore attribute.
        /// </summary>
        /// <param name="newLifetimeObject"> The object to be copied over. </param>
        public override void CopyTo(LifetimeObject newLifetimeObject)
        {
            base.CopyTo(newLifetimeObject);
            var newMaterial = newLifetimeObject as Material;
            if (newMaterial != null)
            {
                newMaterial._isWireframe = _isWireframe;
                newMaterial._isReflective = _isReflective;
                newMaterial._isRefractive = _isRefractive;
                newMaterial._isTranslucent = _isTranslucent;
                newMaterial._sourceBlend = _sourceBlend;
                newMaterial._destinationBlend = _destinationBlend;
            }
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary>
        /// 	Releases all resources used by the Game class.
        /// </summary>
        /// <param name="disposing"> true to release both managed and unmanaged resources; false to release only unmanaged resources. </param>
        protected override void Dispose(bool disposing)
        {
            _isDisposed = true;
            if (disposing)
            {
                _isWireframe = false;
                _isReflective = false;
                _isRefractive = false;
                _isTranslucent = false;
                _sourceBlend = Blend.SourceAlpha;
                _destinationBlend = Blend.InverseSourceColor;
            }
            base.Dispose(disposing);
        }

        #endregion

        #endregion
    }
}